/*
 * Copyright (c) 2012 Andrea De Rinaldis <ozioso at ipignoli.dyndns.org>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package org.dyndns.ipignoli.droidcruise.position;

import org.dyndns.ipignoli.droidcruise.DroidCruise;
import org.dyndns.ipignoli.droidcruise.routes.RouteList;
import org.dyndns.ipignoli.droidcruise.ui.GpsUI;
import org.dyndns.ipignoli.droidcruise.ui.RadarUI;
import org.dyndns.ipignoli.droidcruise.ui.RouteUI;
import android.location.GpsStatus;
import android.location.Location;
import android.location.LocationListener;
import android.os.Bundle;
import android.util.Log;


public class MyLocationListener implements LocationListener, GpsStatus.Listener{
  private static MyLocationListener instance;

  public static MyLocationListener getInstance(RouteList routes,
      RouteUI routeUI, RadarUI radarUI, GpsUI gpsUI,
      CurrentPosition currentPosition, int gpsAccuracy){
    if(instance == null)
      instance = new MyLocationListener();
    instance
        .init(routes, routeUI, radarUI, gpsUI, currentPosition, gpsAccuracy);
    return instance;
  }

  public static MyLocationListener getInstance(){
    return instance;
  }

  private RouteList       routes;
  private RouteUI         routeUI;
  private RadarUI         radarUI;
  private GpsUI           gpsUI;
  private CurrentPosition currentPosition;
  private int             gpsAccuracy;
  private boolean         active;
  private long            lastLocationMsec;

  private MyLocationListener(){
    this.routes = null;
    this.routeUI = null;
    this.radarUI = null;
    this.gpsUI = null;
    this.currentPosition = null;
    this.gpsAccuracy = -1;
    active = false;
    lastLocationMsec = 0;
  }

  private void init(RouteList routes, RouteUI routeUI, RadarUI radarUI,
      GpsUI gpsUI, CurrentPosition currentPosition, int gpsAccuracy){
    setRoutes(routes);
    this.routeUI = routeUI;
    this.radarUI = radarUI;
    this.gpsUI = gpsUI;
    this.currentPosition = currentPosition;
    setMinAccuracy(gpsAccuracy);
    setActive(false);
    lastLocationMsec = 0;
    Log.println(DroidCruise.DEBUG_LEVEL, "droidcruise.MyLocationListener.init",
        "MyLocationListener initialized");
  }

  public void start(){
    setActive(true);
    Log.println(DroidCruise.DEBUG_LEVEL, "droidcruise.MyLocationListener.start",
        "MyLocationListener started");
  }

  public void stop(){
    setActive(false);
    Log.println(DroidCruise.DEBUG_LEVEL, "droidcruise.MyLocationListener.stop",
        "MyLocationListener stopped");
  }

  public synchronized RouteList getRoutes(){
    return routes;
  }

  public synchronized void setRoutes(RouteList routes){
    this.routes = routes;
  }

  public int getMinAccuracy(){
    return gpsAccuracy;
  }

  public void setMinAccuracy(int gpsAccuracy){
    this.gpsAccuracy = gpsAccuracy;
  }

  @Override
  public void onLocationChanged(Location location){
    Log.println(DroidCruise.DEBUG_LEVEL,
        "droidcruise.MyLocationListener.onLocationChanged",
        "MyLocationListener run loop started at " + System.currentTimeMillis());
    if(!isActive())
      return;

    lastLocationMsec = System.currentTimeMillis();

    Log.println(
        DroidCruise.DEBUG_LEVEL,
        "droidcruise.MyLocationListener.onLocationChanged",
        "Location: lon=" + location.getLongitude() + " lat="
            + location.getLatitude() + " time=" + location.getTime()
            + " speed=" + location.getSpeed() + " provider="
            + location.getProvider() + " accuracy=" + location.getAccuracy()
            + " altitude=" + location.getAltitude() + " bearing="
            + location.getBearing());

    if(getRoutes() != null){
      getRoutes().update(location, gpsAccuracy);
      routeUI.setText(getRoutes().getSelectedRoute());
      radarUI.setRadar(getRoutes().getRadar());
    }

    if(location.getAccuracy() > gpsAccuracy)
      gpsUI.setState(GpsUI.INACCURATE);
    else
      gpsUI.setState(GpsUI.AVAILABLE);

    currentPosition.setPosition(new MyLocation(location, getRoutes() == null
        ? null : getRoutes().getSelectedRoute()));

    Log.println(DroidCruise.DEBUG_LEVEL,
        "droidcruise.MyLocationListener.onLocationChanged",
        "MyLocationListener run loop ended at " + System.currentTimeMillis());
  }

  @Override
  public void onStatusChanged(String provider, int status, Bundle extras){}

  @Override
  public void onProviderEnabled(String provider){
    if(!isActive())
      return;
    gpsUI.setState(GpsUI.AVAILABLE);
  }

  @Override
  public void onProviderDisabled(String provider){
    if(!isActive())
      return;
    gpsUI.setState(GpsUI.TEMPORARILY_UNAVAILABLE);
  }

  private synchronized void setActive(boolean active){
    this.active = active;
  }

  private synchronized boolean isActive(){
    return active;
  }

  @Override
  public void onGpsStatusChanged(int event){
    switch(event){
      case GpsStatus.GPS_EVENT_FIRST_FIX:
        gpsUI.setLocked(true);
        return;
      case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
        if(lastLocationMsec + DroidCruise.GPS_NOTIFICATION_MIN_TIME * 3 + 1 < System
            .currentTimeMillis())
          gpsUI.setState(GpsUI.TEMPORARILY_UNAVAILABLE);
        return;
    }
  }
}
